package lt.marsrutai.mobile.android.activity;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import lt.marsrutai.mobile.android.R;
import lt.marsrutai.mobile.android.activity.intent.FastestRoutesResultMapIntentHolder;
import lt.marsrutai.mobile.android.map.overlay.RoutePointBalloonView;
import lt.marsrutai.mobile.android.map.overlay.RoutePointOverlayItem;
import lt.marsrutai.mobile.android.map.overlay.TappableOverlayContainer;
import lt.marsrutai.mobile.android.model.Route;
import lt.marsrutai.mobile.android.model.Segment;
import lt.marsrutai.mobile.android.model.SegmentPoint;
import lt.marsrutai.mobile.android.util.StringUtils;
import android.graphics.drawable.Drawable;
import android.os.Bundle;

import com.google.android.maps.GeoPoint;

/**
 * Displays a route on the map
 * 
 * @author Justas
 */
public class FastestRoutesResultMapActivity extends BaseMapActivity {
    
    private String scopeFolder;
    private Route route;

    public FastestRoutesResultMapActivity() {
        super(false);
    }
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        receiveItemData(savedInstanceState);
        
        displayStops();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        FastestRoutesResultMapIntentHolder intentHolder = new FastestRoutesResultMapIntentHolder(new Bundle());
        intentHolder.setRoute(route);
        intentHolder.setScopeFolder(scopeFolder);
        
        outState.putAll(intentHolder.getIntent().getExtras());
        super.onSaveInstanceState(outState);
    }

    protected void receiveItemData(Bundle bundle) {
        FastestRoutesResultMapIntentHolder intentHolder;

        intentHolder = new FastestRoutesResultMapIntentHolder(bundle != null ? bundle : getIntent().getExtras());

        route = intentHolder.getRoute();
        scopeFolder = intentHolder.getScopeFolder();
    }
    
    private void displayStops() {
        TappableOverlayContainer<RoutePointOverlayItem> stopOverlay = new TappableOverlayContainer<RoutePointOverlayItem>(mapView, getResources().getDrawable(
                R.drawable.all_stops), new RoutePointBalloonView(mapView.getContext()));
        List<RouteNode> routeNodes = createRouteNodes();
        List<GeoPoint> allGeoPoints = new LinkedList<GeoPoint>();
        
        // set markers depending on route node type
        for (int i = 0; i < routeNodes.size(); i++) {
            RouteNode routeNode = routeNodes.get(i);
            GeoPoint point = createGeoPoint(routeNode.getPoint().getLatitude(), routeNode.getPoint().getLongitude());
            allGeoPoints.add(point);

            RoutePointOverlayItem overlayItem = new RoutePointOverlayItem(point, routeNode, scopeFolder);
            stopOverlay.addOverlay(overlayItem);
            
            if (i == 0) {
                overlayItem.setMarker(getStopMarker(R.drawable.first_stop));
            }
            else if (i == routeNodes.size() - 1) {
                overlayItem.setMarker(getStopMarker(R.drawable.last_stop));
            }
            else if (routeNode.getType() == RouteNodeActionType.CHANGE || routeNode.getType() == RouteNodeActionType.GET_IN) {
                overlayItem.setMarker(getStopMarker(R.drawable.segment_start));
            }
            else if (routeNode.getType() == RouteNodeActionType.GET_OUT) {
                overlayItem.setMarker(getStopMarker(R.drawable.segment_end));
            }
        }
        
        mapView.getOverlays().add(stopOverlay);

        // Center map and zoom in to the route
        zoomToItems(allGeoPoints);
    }

    /**
     * Builds a list of nodes representing the current route
     * 
     * @return
     */
    protected List<RouteNode> createRouteNodes() {
        List<RouteNode> routeNodes = new ArrayList<RouteNode>();

        List<Segment> segments = route.getSegments();
        int indexOfLastSegment = segments.size() - 1;
        for (int segmentIndex = 0; segmentIndex <= indexOfLastSegment; segmentIndex++) {
            Segment segment = segments.get(segmentIndex);
            List<SegmentPoint> geoPoints = segment.getGeoPoints();
            int indexOfLastPoint = geoPoints.size() - 1;
            
            for (int pointIndex = 0; pointIndex <= indexOfLastPoint; pointIndex++) {
                SegmentPoint segmentPoint = geoPoints.get(pointIndex);
                
                // Current point is the same as previous - meaning it's the same stop
                if (routeNodes.size() > 0 && StringUtils.equals(routeNodes.get(routeNodes.size() - 1).point.getStopId(), segmentPoint.getStopId())) {
                    RouteNode routeNode = routeNodes.get(routeNodes.size() - 1);
                    
                    RouteNodeActionType type = RouteNodeActionType.CHANGE;
                    
                    if (pointIndex == 0 && previousSegmentExists(segmentIndex) && isWalkSegment(segments.get(segmentIndex - 1))) {
                        type = RouteNodeActionType.GET_IN;
                    }
                    else if (pointIndex == 0 && isWalkSegment(segments.get(segmentIndex))) {
                        type = RouteNodeActionType.GET_OUT;
                    }
                    
                    routeNode.setType(type);
                    routeNode.setTransportId2(segment.getTransportId());
                    routeNode.setScheduleName2(segment.getScheduleName());
                }
                else {
                    RouteNode routeNode = new RouteNode();
                    routeNode.setType(RouteNodeActionType.RIDE);
                    routeNode.setPoint(segmentPoint);
                    routeNode.setTransportId(segment.getTransportId());
                    routeNode.setScheduleName(segment.getScheduleName());
                    boolean firstStop = pointIndex == 0 && segmentIndex == 0;
                    boolean lastStop = pointIndex == indexOfLastPoint && segmentIndex == indexOfLastSegment;
                    routeNode.setAlternativeName(firstStop ? segment.getStartName() : (lastStop ? segment.getEndName() : null));
                    routeNodes.add(routeNode);
                }
            }
        }
        return routeNodes;
    }

    private boolean previousSegmentExists(int segmentIndex) {
        return segmentIndex > 0;
    }

    protected boolean isWalkSegment(Segment segment) {
        return segment.getTrackid() == null;
    }

    private Drawable getStopMarker(int drawableId) {
        Drawable marker = getResources().getDrawable(drawableId);
        return TappableOverlayContainer.boundCenterBottom(marker);
    }
    
    public static class RouteNode {
        
        private RouteNodeActionType type;
        private SegmentPoint point;
        private String transportId;
        private String scheduleName;

        private String transportId2;
        private String scheduleName2;
        
        private String alternativeName;

        public SegmentPoint getPoint() {
            return point;
        }

        public void setPoint(SegmentPoint point) {
            this.point = point;
        }

        public void setType(RouteNodeActionType type) {
            this.type = type;
        }

        public RouteNodeActionType getType() {
            return type;
        }

        public String getTransportId() {
            return transportId;
        }

        public void setTransportId(String transportId) {
            this.transportId = transportId;
        }

        public String getTransportId2() {
            return transportId2;
        }

        public void setTransportId2(String transportId2) {
            this.transportId2 = transportId2;
        }

        public String getScheduleName() {
            return scheduleName;
        }

        public void setScheduleName(String scheduleName) {
            this.scheduleName = scheduleName;
        }

        public String getScheduleName2() {
            return scheduleName2;
        }

        public void setScheduleName2(String scheduleName2) {
            this.scheduleName2 = scheduleName2;
        }

        public String getAlternativeName() {
            return alternativeName;
        }

        public void setAlternativeName(String alternativeName) {
            this.alternativeName = alternativeName;
        }

    }
    
    public enum RouteNodeActionType {
        RIDE, CHANGE, GET_OUT, GET_IN
    }

}
